home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-9.10-netbook-remix-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / glchess / game.py < prev    next >
Text File  |  2009-09-22  |  25KB  |  813 lines

  1. # -*- coding: utf-8 -*-
  2. """
  3. """
  4.  
  5. __author__ = 'Robert Ancell <bob27@users.sourceforge.net>'
  6. __license__ = 'GNU General Public License Version 2'
  7. __copyright__ = 'Copyright 2005-2006  Robert Ancell'
  8.  
  9. import chess.board
  10. import chess.san
  11.  
  12. # Game results
  13. RESULT_IN_PROGRESS         = '*'
  14. RESULT_WHITE_WINS          = '1-0'
  15. RESULT_BLACK_WINS          = '0-1'
  16. RESULT_DRAW                = '1/2-1/2'
  17.  
  18. # Reasons for the result
  19. RULE_CHECKMATE             = 'CHECKMATE'
  20. RULE_STALEMATE             = 'STALEMATE'
  21. RULE_TIMEOUT               = 'TIMEOUT'
  22. RULE_FIFTY_MOVES           = 'FIFTY_MOVES'
  23. RULE_THREE_FOLD_REPETITION = 'THREE_FOLD_REPETITION'
  24. RULE_INSUFFICIENT_MATERIAL = 'INSUFFICIENT_MATERIAL'
  25. RULE_RESIGN                = 'RESIGN'
  26. RULE_DEATH                 = 'DEATH'
  27. RULE_AGREEMENT             = 'AGREEMENT'
  28. RULE_ABANDONMENT           = 'ABANDONMENT'
  29.  
  30. class ChessMove:
  31.     """
  32.     """
  33.  
  34.     # The move number (game starts at 0)
  35.     number     = 0
  36.     
  37.     # The player and piece that moved
  38.     player     = None
  39.     piece      = None
  40.     
  41.     # The piece that was promoted to (or None)
  42.     promotion  = None
  43.     
  44.     # The victim piece (or None)
  45.     victim     = None
  46.  
  47.     # The start and end position of the move
  48.     start      = None
  49.     end        = None
  50.     
  51.     # The move in CAN and SAN format
  52.     canMove    = ''
  53.     sanMove    = ''
  54.  
  55.     # The game result after this move
  56.     opponentInCheck = False
  57.     opponentCanMove = False
  58.     
  59.     # If this move can be used as a resignation
  60.     fiftyMoveRule = False
  61.     threeFoldRepetition = False
  62.     
  63.     # A comment about this move
  64.     comment = ''
  65.  
  66.     # Numeric annotation glyph for move
  67.     nag     = ''
  68.  
  69. class ChessPlayer:
  70.     """
  71.     """
  72.  
  73.     def __init__(self, name):
  74.         """Constructor for a chess player.
  75.  
  76.         'name' is the name of the player.
  77.         """
  78.         self.__name = str(name)
  79.         self.__game = None
  80.         self.__readyToMove = False
  81.         self.isAlive = True
  82.  
  83.     # Methods to extend
  84.  
  85.     def onPieceMoved(self, piece, start, end, delete):
  86.         """Called when a chess piece is moved.
  87.         
  88.         'piece' is the piece that has been moved (chess.board.ChessPiece).
  89.         'start' is the location the piece in LAN format (string) or None if the piece has been created.
  90.         'end' is the location the piece has moved to in LAN format (string).
  91.         'delete' is a flag to show if the piece should be deleted when it arrives there (boolean).
  92.         """
  93.         pass
  94.     
  95.     def onPlayerMoved(self, player, move):
  96.         """Called when a player has moved.
  97.         
  98.         'player' is the player that has moved (ChessPlayer).
  99.         'move' is the record for this move (ChessMove).
  100.         """
  101.         pass
  102.     
  103.     def onUndoMove(self):
  104.         pass
  105.     
  106.     def onPlayerStartTurn(self, player):
  107.         pass
  108.  
  109.     def onGameEnded(self, game):
  110.         """Called when a chess game has ended.
  111.         
  112.         'game' is the game that has ended (Game).
  113.         """
  114.         pass
  115.     
  116.     def readyToMove(self):
  117.         """FIXME
  118.         """
  119.         pass
  120.  
  121.     # Public methods
  122.  
  123.     def getName(self):
  124.         """Get the name of this player.
  125.         
  126.         Returns the player name (string).
  127.         """
  128.         return self.__name
  129.     
  130.     def getGame(self):
  131.         """Get the game this player is in.
  132.         
  133.         Returns the game (Game) or None if not in a game.
  134.         """
  135.         return self.__game
  136.     
  137.     def getRemainingTime(self):
  138.         """Get the amount of time this player has remaining.
  139.         
  140.         Returns the amount of time in milliseconds.
  141.         """
  142.         if self is self.__game.getWhite():
  143.             timer = self.__game.whiteTimer
  144.         elif self is self.__game.getBlack():
  145.             timer = self.__game.blackTimer
  146.         else:
  147.             return 0
  148.         
  149.         if timer is None:
  150.             return 0
  151.         else:
  152.             return timer.controller.getRemaining()
  153.  
  154.     def isReadyToMove(self):
  155.         """
  156.         """
  157.         return self.__readyToMove
  158.     
  159.     def canMove(self, start, end, promotionType = chess.board.QUEEN):
  160.         """
  161.         """
  162.         return self.__game.canMove(self, start, end, promotionType)
  163.  
  164.     def move(self, move):
  165.         """Move a piece.
  166.         
  167.         'move' is the move to make in Normal/Long/Standard Algebraic format (string).
  168.         """
  169.         self.__game.move(self, move)
  170.         
  171.     def undo(self):
  172.         """Undo moves until it is this players turn"""
  173.         self.__game.undo(self)
  174.         
  175.     def endMove(self):
  176.         """Complete this players turn"""
  177.         self.__game.endMove(self)
  178.         
  179.     def resign(self):
  180.         """Resign from the game"""
  181.         self.__game.resign(self)
  182.         
  183.     def claimDraw(self):
  184.         """Claim a draw"""
  185.         return self.__game.claimDraw()
  186.  
  187.     def outOfTime(self):
  188.         """Report this players timer has expired"""
  189.         self.__game.outOfTime(self)
  190.         
  191.     def die(self):
  192.         """Report this player has died"""
  193.         self.isAlive = False
  194.         if self.__game is not None:
  195.             self.__game.killPlayer(self)
  196.  
  197.     # Private methods
  198.     
  199.     def _setGame(self, game):
  200.         """
  201.         """
  202.         self.__game = game
  203.         
  204.     def _setReadyToMove(self, readyToMove):
  205.         if self.__readyToMove == readyToMove:
  206.             return
  207.         self.__readyToMove = readyToMove
  208.         if readyToMove is True:
  209.             self.readyToMove()
  210.  
  211. class ChessGameBoard(chess.board.ChessBoard):
  212.     """
  213.     """
  214.     
  215.     def __init__(self, game):
  216.         """
  217.         """
  218.         self.__game = game
  219.         chess.board.ChessBoard.__init__(self)
  220.  
  221.     def onPieceMoved(self, piece, start, end, delete):
  222.         """Called by chess.board.ChessBoard"""
  223.         self.__game._onPieceMoved(piece, start, end, delete)
  224.  
  225. class ChessGameSANConverter(chess.san.SANConverter):
  226.     """
  227.     """
  228.         
  229.     __colourToSAN = {chess.board.WHITE: chess.san.SANConverter.WHITE,
  230.                      chess.board.BLACK: chess.san.SANConverter.BLACK}
  231.     __sanToColour = {}
  232.     for (a, b) in __colourToSAN.iteritems():
  233.         __sanToColour[b] = a
  234.         
  235.     __typeToSAN = {chess.board.PAWN:   chess.san.SANConverter.PAWN,
  236.                    chess.board.KNIGHT: chess.san.SANConverter.KNIGHT,
  237.                    chess.board.BISHOP: chess.san.SANConverter.BISHOP,
  238.                    chess.board.ROOK:   chess.san.SANConverter.ROOK,
  239.                    chess.board.QUEEN:  chess.san.SANConverter.QUEEN,
  240.                    chess.board.KING:   chess.san.SANConverter.KING}
  241.     __sanToType = {}
  242.     for (a, b) in __typeToSAN.iteritems():
  243.         __sanToType[b] = a
  244.         
  245.     def __init__(self, board, moveNumber):
  246.         self.board = board
  247.         self.moveNumber = moveNumber
  248.         chess.san.SANConverter.__init__(self)
  249.     
  250.     def decode(self, colour, move):
  251.         (start, end, result, promotionType) = chess.san.SANConverter.decode(self, self.__colourToSAN[colour], move)
  252.         return (start, end, self.__sanToType[promotionType])
  253.     
  254.     def encode(self, start, end, isTake, promotionType):
  255.         if promotionType is None:
  256.             promotion = self.QUEEN
  257.         else:
  258.             promotion = self.__typeToSAN[promotionType]
  259.         return chess.san.SANConverter.encode(self, start, end, isTake, promotion)
  260.  
  261.     def getPiece(self, location):
  262.         """Called by chess.san.SANConverter"""
  263.         piece = self.board.getPiece(location, self.moveNumber)
  264.         if piece is None:
  265.             return None
  266.         return (self.__colourToSAN[piece.getColour()], self.__typeToSAN[piece.getType()])
  267.     
  268.     def testMove(self, colour, start, end, promotionType, allowSuicide = False):
  269.         """Called by chess.san.SANConverter"""
  270.         move = self.board.testMove(self.__sanToColour[colour], start, end,
  271.                                      self.__sanToType[promotionType], allowSuicide, self.moveNumber)
  272.         if move is None:
  273.             return False
  274.  
  275.         if move.opponentInCheck:
  276.             if not move.opponentCanMove:
  277.                 return chess.san.SANConverter.CHECKMATE
  278.             return chess.san.SANConverter.CHECK
  279.         return True
  280.  
  281. class ChessGame:
  282.     """
  283.     """    
  284.  
  285.     def __init__(self):
  286.         """Game constructor"""
  287.         self.__players = []
  288.         self.__spectators = []
  289.         self.__whitePlayer = None
  290.         self.__blackPlayer = None
  291.         self.__currentPlayer = None
  292.         self.__moves = []
  293.         self.__inCallback = False
  294.         self.__queuedCalls = []
  295.         self.board = ChessGameBoard(self)
  296.  
  297.         self.__started = False
  298.         self.result  = RESULT_IN_PROGRESS
  299.         self.rule    = None    
  300.         self.whiteTimer = None
  301.         self.blackTimer = None
  302.         
  303.     def getAlivePieces(self, moveNumber = -1):
  304.         """Get the alive pieces on the board.
  305.         
  306.         'moveNumber' is the move to get the pieces from (integer).
  307.         
  308.         Returns a dictionary of the alive pieces (board.ChessPiece) keyed by location.
  309.         Raises an IndexError exception if moveNumber is invalid.
  310.         """
  311.         return self.board.getAlivePieces(moveNumber)
  312.     
  313.     def getDeadPieces(self, moveNumber = -1):
  314.         """Get the dead pieces from the game.
  315.         
  316.         'moveNumber' is the move to get the pieces from (integer).
  317.         
  318.         Returns a list of the pieces (board.ChessPiece) in the order they were killed.
  319.         Raises an IndexError exception if moveNumber is invalid.
  320.         """
  321.         return self.board.getDeadPieces(moveNumber)
  322.     
  323.     def setTimers(self, whiteTimer, blackTimer):
  324.         """
  325.         """
  326.         self.whiteTimer = whiteTimer
  327.         self.blackTimer = blackTimer
  328.  
  329.     def setWhite(self, player):
  330.         """Set the white player in the game.
  331.         
  332.         'player' is the player to use as white.
  333.         
  334.         If the game has started or there is a white player an exception is thrown.
  335.         """
  336.         assert(self.__started is False)
  337.         assert(self.__whitePlayer is None)
  338.         self.__whitePlayer = player
  339.         self.__connectPlayer(player)
  340.  
  341.     def getWhite(self):
  342.         """Returns the current white player (player.Player)"""
  343.         return self.__whitePlayer
  344.     
  345.     def setBlack(self, player):
  346.         """Set the black player in the game.
  347.         
  348.         'player' is the player to use as black.
  349.         
  350.         If the game has started or there is a black player an exception is thrown.
  351.         """
  352.         assert(self.__started is False)
  353.         assert(self.__blackPlayer is None)
  354.         self.__blackPlayer = player
  355.         self.__connectPlayer(player)
  356.         
  357.     def getBlack(self):
  358.         """Returns the current white player (player.Player)"""
  359.         return self.__blackPlayer
  360.     
  361.     def getCurrentPlayer(self):
  362.         """Get the player to move"""
  363.         return self.__currentPlayer
  364.     
  365.     def addSpectator(self, player):
  366.         """Add a spectator to the game.
  367.         
  368.         'player' is the player spectating.
  369.         
  370.         This can be called after the game has started.
  371.         """
  372.         self.__spectators.append(player)
  373.         self.__connectPlayer(player)
  374.  
  375.     def isStarted(self):
  376.         """Returns True if the game has been started"""
  377.         return self.__started
  378.         
  379.     def start(self, moves = []):
  380.         """Start the game.
  381.         
  382.         'moves' is a list of moves to start with.
  383.         
  384.         If there is no white or black player then an exception is raised.
  385.         """
  386.         assert(self.__whitePlayer is not None and self.__blackPlayer is not None)
  387.         
  388.         # Disabled for now
  389.         #import network
  390.         #self.x = network.GameReporter('Test Game', 12345)
  391.         #print 'Reporting'
  392.  
  393.         # Load starting moves
  394.         self.__currentPlayer = self.__whitePlayer
  395.         for move in moves:
  396.             self.move(self.__currentPlayer, move)
  397.             if self.__currentPlayer is self.__whitePlayer:
  398.                 self.__currentPlayer = self.__blackPlayer
  399.             else:
  400.                 self.__currentPlayer = self.__whitePlayer
  401.  
  402.         self.__started = True
  403.  
  404.         # Stop if both players aren't alive
  405.         if not self.__whitePlayer.isAlive:
  406.             self.killPlayer(self.__whitePlayer)
  407.             return
  408.         if not self.__blackPlayer.isAlive:
  409.             self.killPlayer(self.__blackPlayer)
  410.             return
  411.  
  412.         # Stop if game ended on loaded moves
  413.         if self.result != RESULT_IN_PROGRESS:
  414.             self._notifyEndGame()
  415.             return
  416.  
  417.         self.startLock()
  418.         
  419.         # Inform other players of the result
  420.         for player in self.__players:
  421.             player.onPlayerStartTurn(self.__currentPlayer)
  422.  
  423.         # Get the next player to move
  424.         self.__currentPlayer._setReadyToMove(True)
  425.  
  426.         self.endLock()
  427.  
  428.     def getSquareOwner(self, coord):
  429.         """TODO
  430.         """
  431.         piece = self.board.getPiece(coord)
  432.         if piece is None:
  433.             return None
  434.         
  435.         colour = piece.getColour()
  436.         if colour is chess.board.WHITE:
  437.             return self.__whitePlayer
  438.         elif colour is chess.board.BLACK:
  439.             return self.__blackPlayer
  440.         else:
  441.             return None
  442.         
  443.     def canMove(self, player, start, end, promotionType):
  444.         """Test if a player can move.
  445.         
  446.         'player' is the player making the move.
  447.         'start' is the location to move from in LAN format (string).
  448.         'end' is the location to move from in LAN format (string).
  449.         'promotionType' is the piece type to promote pawns to. FIXME: Make this a property of the player
  450.         
  451.         Return True if can move, otherwise False.
  452.         """
  453.         if player is not self.__currentPlayer:
  454.             return False
  455.         
  456.         if player is self.__whitePlayer:
  457.             colour = chess.board.WHITE
  458.         elif player is self.__blackPlayer:
  459.             colour = chess.board.BLACK
  460.         else:
  461.             assert(False)
  462.  
  463.         move = self.board.testMove(colour, start, end, promotionType = promotionType)
  464.  
  465.         return move is not None
  466.     
  467.     def move(self, player, move):
  468.         """Get a player to make a move.
  469.         
  470.         'player' is the player making the move.
  471.         'move' is the move to make in SAN or LAN format (string).
  472.         """
  473.         if self.__inCallback:
  474.             self.__queuedCalls.append((self.move, (player, move)))
  475.             return
  476.         
  477.         self.startLock()
  478.         
  479.         if player is not self.__currentPlayer:
  480.             print 'Player attempted to move out of turn'
  481.         else:
  482.             self._move(player, move)
  483.  
  484.         self.endLock()
  485.  
  486.     def undo(self, player):
  487.         if self.__inCallback:
  488.             self.__queuedCalls.append((self.undo, (player,)))
  489.             return
  490.         
  491.         self.startLock()
  492.         
  493.         self.__whitePlayer._setReadyToMove(False)
  494.         self.__blackPlayer._setReadyToMove(False)
  495.         
  496.         # Pretend the current player is the oponent so when endMove() is called
  497.         # this player will become the active player.
  498.         # Yes, this IS a big hack...
  499.         if player is self.__whitePlayer:
  500.             self.__currentPlayer = self.__blackPlayer
  501.         else:
  502.             self.__currentPlayer = self.__whitePlayer
  503.         
  504.         # If this player hasn't moved then undo oponents move before their one
  505.         if len(self.__moves) > 0 and self.__moves[-1].player is not player:
  506.             count = 2
  507.         else:
  508.             count = 1
  509.             
  510.         for i in xrange(count):
  511.             if len(self.__moves) != 0:
  512.                 self.board.undo()
  513.                 self.__moves = self.__moves[:-1]
  514.                 for p in self.__players:
  515.                     p.onUndoMove()
  516.         
  517.         self.endLock()
  518.         
  519.     def startLock(self):
  520.         assert(self.__inCallback is False)
  521.         self.__inCallback = True
  522.         
  523.     def endLock(self):
  524.         self.__inCallback = False
  525.         while len(self.__queuedCalls) > 0:
  526.             (call, args) = self.__queuedCalls[0]
  527.             self.__queuedCalls = self.__queuedCalls[1:]
  528.             call(*args)
  529.  
  530.     def _move(self, player, move):
  531.         """
  532.         """
  533.         if self.result != RESULT_IN_PROGRESS:
  534.             print 'Game completed'
  535.             return
  536.         
  537.         if self.__currentPlayer is self.__whitePlayer:
  538.             colour = chess.board.WHITE
  539.         else:
  540.             colour = chess.board.BLACK
  541.  
  542.         # If move is SAN process it as such
  543.         try:
  544.             (start, end, _, _, promotionType, _) = chess.lan.decode(colour, move)
  545.         except chess.lan.DecodeError, e:
  546.             converter = ChessGameSANConverter(self.board, len(self.__moves))
  547.             try:
  548.                 (start, end, promotionType) = converter.decode(colour, move)
  549.             except chess.san.Error, e:
  550.                 print 'Invalid move: ' + move
  551.                 return
  552.  
  553.         # Only use promotion type if a pawn move to far file
  554.         piece = self.board.getPiece(start)
  555.         promotion = None
  556.         if piece is not None and piece.getType() is chess.board.PAWN:
  557.             if colour is chess.board.WHITE:
  558.                 if end[1] == '8':
  559.                     promotion = promotionType
  560.             else:
  561.                 if end[1] == '1':
  562.                     promotion = promotionType
  563.  
  564.         moveResult = self.board.movePiece(colour, start, end, promotionType)
  565.         if moveResult is None:
  566.             print 'Illegal move: ' + str(move)
  567.             return
  568.         
  569.         # Re-encode for storing and reporting
  570.         canMove = chess.lan.encode(colour, start, end, promotionType = promotion)
  571.         converter = ChessGameSANConverter(self.board, len(self.__moves))
  572.         try:
  573.             sanMove = converter.encode(start, end, moveResult.victim != None, promotionType)
  574.         except chess.san.Error:
  575.             # If for some reason we couldn't make the SAN move the use the CAN move instead
  576.             sanMove = canMove
  577.  
  578.         m = ChessMove()
  579.         if len(self.__moves) == 0:
  580.             m.number = 1
  581.         else:
  582.             m.number = self.__moves[-1].number + 1
  583.         m.player              = self.__currentPlayer
  584.         m.piece               = piece
  585.         m.victim              = moveResult.victim
  586.         m.start               = start
  587.         m.end                 = end
  588.         m.canMove             = canMove
  589.         m.sanMove             = sanMove
  590.         m.opponentInCheck     = moveResult.opponentInCheck
  591.         m.opponentCanMove     = moveResult.opponentCanMove
  592.         m.fiftyMoveRule       = moveResult.fiftyMoveRule
  593.         m.threeFoldRepetition = moveResult.threeFoldRepetition
  594.         #FIXME: m.comment             = move.comment
  595.         #FIXME: m.nag                 = move.nag
  596.  
  597.         self.__moves.append(m)
  598.  
  599.         # This player has now moved
  600.         self.__currentPlayer._setReadyToMove(False)
  601.  
  602.         # Inform other players of the result
  603.         for player in self.__players:
  604.             player.onPlayerMoved(self.__currentPlayer, m)
  605.  
  606.         # Check if the game has ended
  607.         result = RESULT_IN_PROGRESS
  608.         if not m.opponentCanMove:
  609.             if self.__currentPlayer is self.__whitePlayer:
  610.                 result = RESULT_WHITE_WINS
  611.             else:
  612.                 result = RESULT_BLACK_WINS
  613.             if m.opponentInCheck:
  614.                 rule = RULE_CHECKMATE
  615.             else:
  616.                 result = RESULT_DRAW
  617.                 rule = RULE_STALEMATE
  618.  
  619.         # Check able to complete
  620.         if not self.board.sufficientMaterial():
  621.             result = RESULT_DRAW
  622.             rule = RULE_INSUFFICIENT_MATERIAL
  623.  
  624.         if result is not RESULT_IN_PROGRESS:
  625.             self.endGame(result, rule)
  626.  
  627.     def endMove(self, player):
  628.         """
  629.         """
  630.         if self.__inCallback:
  631.             self.__queuedCalls.append((self.endMove, (player,)))
  632.             return
  633.         
  634.         if player is not self.__currentPlayer:
  635.             print 'Player attempted to move out of turn'
  636.             return
  637.         if player.move is None:
  638.             print "Ending move when haven't made one"
  639.             return
  640.  
  641.         if self.__currentPlayer is self.__whitePlayer:
  642.             self.__currentPlayer = self.__blackPlayer
  643.         else:
  644.             self.__currentPlayer = self.__whitePlayer
  645.         
  646.         self.startLock()
  647.         
  648.         # Inform other players of the result
  649.         for player in self.__players:
  650.             player.onPlayerStartTurn(self.__currentPlayer)
  651.  
  652.         # Notify the next player they can move
  653.         if self.__started is True and self.result == RESULT_IN_PROGRESS:
  654.             self.__currentPlayer._setReadyToMove(True)
  655.  
  656.         self.endLock()
  657.  
  658.     def resign(self, player):
  659.         """Get a player to resign.
  660.         
  661.         'player' is the player resigning.
  662.         """
  663.         rule = RULE_RESIGN
  664.         if player is self.__whitePlayer:
  665.             self.endGame(RESULT_BLACK_WINS, rule)
  666.         else:
  667.             self.endGame(RESULT_WHITE_WINS, rule)
  668.             
  669.     def claimDraw(self):
  670.         """
  671.         """
  672.         # TODO: Penalise if make an incorrect attempt
  673.         try:
  674.             move = self.__moves[-1]
  675.         except IndexError:
  676.             return False
  677.         else:
  678.             if move.fiftyMoveRule:
  679.                 rule = RULE_FIFTY_MOVES
  680.             elif move.threeFoldRepetition:
  681.                 rule = RULE_THREE_FOLD_REPETITION
  682.             else:
  683.                 return False
  684.  
  685.         self.endGame(RESULT_DRAW, rule)
  686.         return True
  687.  
  688.     def killPlayer(self, player):
  689.         """Report a player has died
  690.         
  691.         'player' is the player that has died.
  692.         """
  693.         if player is self.__whitePlayer:
  694.             result = RESULT_BLACK_WINS
  695.         elif player is self.__blackPlayer:
  696.             result = RESULT_WHITE_WINS       
  697.         self.endGame(result, RULE_DEATH)
  698.  
  699.     def outOfTime(self, player):
  700.         """Report a player's timer has expired"""
  701.         if player is self.__whitePlayer:
  702.             result = RESULT_BLACK_WINS
  703.         elif player is self.__blackPlayer:
  704.             result = RESULT_WHITE_WINS
  705.         else:
  706.             assert(False)
  707.         self.endGame(result, RULE_TIMEOUT)
  708.         
  709.     def abandon(self):
  710.         self.endGame(RESULT_DRAW, RULE_ABANDONMENT)
  711.  
  712.     def endGame(self, result, rule):
  713.         if self.result != RESULT_IN_PROGRESS:
  714.             return
  715.         self.result = result
  716.         self.rule = rule
  717.         if self.isStarted():
  718.             self._notifyEndGame()
  719.  
  720.     def _notifyEndGame(self):
  721.         self.__currentPlayer._setReadyToMove(False)
  722.         for player in self.__players:
  723.             player.onGameEnded(self)
  724.  
  725.     def getMoves(self):
  726.         """
  727.         """
  728.         return self.__moves
  729.  
  730.     def abort(self):
  731.         """End the game"""
  732.         # Inform players
  733.         for player in self.__players:
  734.             player.onGameEnded(self)
  735.  
  736.     # Private methods:
  737.  
  738.     def __connectPlayer(self, player):
  739.         """Add a player into the game.
  740.         
  741.         'player' is the player to add.
  742.         
  743.         The player will be notified of the current state of the board.
  744.         """
  745.         self.__players.append(player)
  746.         player._setGame(self)
  747.         
  748.         # Notify the player of the current state
  749.         # FIXME: Make the board iteratable...
  750.         for file in '12345678':
  751.             for rank in 'abcdefgh':
  752.                 coord = rank + file
  753.                 piece = self.board.getPiece(coord)
  754.                 if piece is None:
  755.                     continue
  756.  
  757.                 # These are moves from nowhere to their current location
  758.                 player.onPieceMoved(piece, None, coord, False)
  759.  
  760.     def _onPieceMoved(self, piece, start, end, delete):
  761.         """Called by the chess board"""
  762.         
  763.         # Notify all players of creations and deletions
  764.         # NOTE: Normal moves are done above since the SAN moves are calculated before the move...
  765.         # FIXME: Change this so the SAN moves are done afterwards...
  766.         for player in self.__players:
  767.             player.onPieceMoved(piece, start, end, delete)
  768.  
  769. class NetworkChessGame(ChessGame):
  770.     """
  771.     """
  772.     
  773.     def move(self, player, move):
  774.         """Get a player to make a move.
  775.         
  776.         'player' is the player making the move.
  777.         'move' is the move to make. It can be of the form:
  778.                A coordinate move in the form ((file0, rank0), (file1, rank1), promotionType) ((int, int), (int, int), chess.board.PIECE_TYPE) or
  779.                A SAN move (string).
  780.         """
  781.         # Send to the server
  782.         
  783.             
  784. if __name__ == '__main__':
  785.     game = ChessGame()
  786.     
  787.     import pgn
  788.     
  789.     p = pgn.PGN('black.pgn')
  790.     g = p.getGame(0)
  791.  
  792.     class PGNPlayer(ChessPlayer):
  793.  
  794.         def __init__(self, isWhite):
  795.             self.__isWhite = isWhite
  796.             self.__moveNumber = 1
  797.         
  798.         def readyToMove(self):
  799.             if self.__isWhite:
  800.                 move = g.getWhiteMove(self.__moveNumber)
  801.             else:
  802.                 move = g.getBlackMove(self.__moveNumber)
  803.             self.__moveNumber += 1
  804.             self.move(move)
  805.             
  806.     white = PGNPlayer(True)
  807.     black = PGNPlayer(False)
  808.     
  809.     game.setWhite(white)
  810.     game.setBlack(black)
  811.     
  812.     game.start()
  813.